package com.suray.wcs.service.thread;

import com.google.common.base.Joiner;
import com.suray.basic.wcs.path.core.Lifter;
import com.suray.basic.wcs.path.core.Node;
import com.suray.basic.wcs.path.enums.NodeStatus;
import com.suray.basic.wcs.plc.core.Plc;
import com.suray.basic.wcs.plc.entity.PlcDeviceDB;
import com.suray.basic.wcs.plc.enums.Location;
import com.suray.basic.wcs.plc.enums.PlcDeviceType;
import com.suray.basic.wcs.plc.pojo.oper.single.DeviceInterface;
import com.suray.basic.wcs.plc.pojo.oper.single.MonomerConveyorOper;
import com.suray.basic.wcs.plc.pojo.oper.single.TransPlanter;
import com.suray.basic.wcs.plc.service.impl.PlcDeviceDBServiceImpl;
import com.suray.basic.wcs.plc.pojo.DeviceBase;
import com.suray.basic.wcs.plc.pojo.oper.lifter.LifterOper;
import com.suray.basic.wcs.plc.pojo.oper.StationOper;
import com.suray.basic.wcs.plc.pojo.oper.lifter.TwoDirectionRgvLifterOper;
import com.suray.basic.wcs.plc.utils.PlcDeviceProcess;
import com.suray.basic.wcs.utils.Coord;
import com.suray.basic.wcs.utils.LoggerUtil;
import com.suray.basic.wcs.utils.WcsFuncUtil;
import com.suray.commin.core.utils.SpringContextHolder;
import com.suray.wcs.service.core.util.LockNodeManager;
import com.suray.wcs.service.enums.*;
import com.suray.wcs.service.init.SystemInit;
import com.suray.wcs.service.plc.ConveyEvent;
import com.suray.wcs.service.plc.LifterPathNode;
import com.suray.wcs.service.plc.util.LifterEventInterface;
import com.suray.wcs.service.system.bo.NotificationBO;
import com.suray.wcs.service.system.entity.*;
import com.suray.basic.wcs.plc.mapper.PlcDeviceDBMapper;
import com.suray.wcs.service.system.service.impl.*;
import com.suray.wcs.service.thread.process.PlcConveryTask;
import com.suray.wcs.service.util.WcsUtil;
import com.suray.wcs.service.wmsconnect.TaskStatusRtn;
import com.suray.wcs.service.wmsconnect.ToWMSImpl;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class PlcTaskThread extends Thread {
    private TaskDB taskDB;
    private NotificationServiceImpl notificationService = SpringContextHolder.getBean(NotificationServiceImpl.class);
    private ToWMSImpl toWMS = SpringContextHolder.getBean("toWMSImpl");

    private TaskDBServiceImpl taskdbServiceImpl = SpringContextHolder.getBean(TaskDBServiceImpl.class);
    private NodeDBServiceImpl nodeService = SpringContextHolder.getBean(NodeDBServiceImpl.class);

    private TaskDetailServiceImpl taskDetailService = SpringContextHolder.getBean(TaskDetailServiceImpl.class);
    private PlcDeviceDBMapper plcdevicedbMapper = SpringContextHolder.getBean(PlcDeviceDBMapper.class);
    private final PlcDeviceDBServiceImpl plcDeviceDBService = SpringContextHolder.getBean(PlcDeviceDBServiceImpl.class);
    private static Logger log = LoggerFactory.getLogger(PlcTaskThread.class);
    private PortDBServiceImpl portService = SpringContextHolder.getBean(PortDBServiceImpl.class);

    private PlcTaskThread(TaskDB taskDB) {
        setName("Plc线程任务号:" + taskDB.getTaskId());
        this.taskDB = taskDB;
    }

    public static void getInstance(TaskDB taskDB) {
        log.info("任务：" + taskDB.getTaskId() + "开始执行PLC阶段任务");
        Plc.PLC_INTERRUPT_CACHE.put(String.valueOf(taskDB.getTaskId()), String.valueOf(taskDB.getTaskId()));
        new PlcTaskThread(taskDB).start();

    }

    private class PathObject {
        Integer start;
        Integer end;
        Integer nextObj;
        PathObject preObj;

        public PathObject(Integer start, Integer end, Integer nextObj, PathObject prePathObj) {
            this.start = start;
            this.end = end;
            this.nextObj = nextObj;

            if (prePathObj != null) {
                PlcDeviceProcess startDeviceProcess = PlcDeviceProcess.parseDeviceProcess(prePathObj.start);
                PlcDeviceProcess endDeviceProcess = PlcDeviceProcess.parseDeviceProcess(prePathObj.end);

                if (startDeviceProcess == PlcDeviceProcess.INVALID && endDeviceProcess == PlcDeviceProcess.INVALID) {
                    this.preObj = prePathObj;
                }
            }
        }

        public Integer getStart() {
            return start;
        }

        public Integer getNextObj() {
            return nextObj;
        }

        public Integer getPreObj() {
            return preObj == null ? null : preObj.end;
        }

        public void setStart(Integer start) {
            this.start = start;
        }

        public Integer getEnd() {
            return end;
        }

        public void setEnd(Integer end) {
            this.end = end;
        }

        @Override
        public String toString() {
            return "PathObject{" +
                    "start=" + start +
                    ", end=" + end +
                    ", nextObj=" + nextObj +
                    ", preObj=" + (preObj == null ? null : preObj.end) +
                    '}';
        }
    }

    @Override
    public void run() {
        //一. 通过plc起点+任务类型+库区 找到方案表
        //二. 获取到执行方案
        //三. 遍历方案
        //四. 输送线ID，提升机执行ID
        //五. 查PLC设备表拿到名字或者坐标
        try {
            TaskType taskType = taskDB.selectTaskTypeEnum();
            List<ProgrammeDB> programmeDBs;
            PortDB portDB = portService.selectByPortCode(taskDB.getTaskPortId());
            if (taskType.getTypeNum() != TaskType.MOVE_STATION.getTypeNum()) {
                if (taskType.getTypeNum() == TaskType.IN.getTypeNum()) {
                    programmeDBs = SystemInit.getProgrammeByPortAndTypeAndCoord(portDB.getPortCode(), taskDB.selectStartNode().toString(), PortModel.IN_PORT);
                } else {
                    programmeDBs = SystemInit.getProgrammeByPortAndTypeAndCoord(portDB.getPortCode(), taskDB.selectEndNode().toString(), PortModel.OUT_PORT);
                }
                if (programmeDBs != null && programmeDBs.size() != 0) {
                    taskdbServiceImpl.updateTaskByTaskProgrammeGridId(taskDB, programmeDBs.get(0).getProgrammeId().toString());
                    findProgramme(programmeDBs.get(0));
                } else {
                    log.error("该任务无法查找到可行使方案，请检查配置");
                    throw new RuntimeException("该任务无法查找到可行使方案，请检查配置");
                }
            } else {
                moveStationPlc();
            }
            if (taskType.equals(TaskType.OUT) || taskType.equals(TaskType.MOVE_STATION)) {
                taskdbServiceImpl.updateTaskStatus(taskDB, TaskStatus.FINISH);
                String value = Plc.PLC_INTERRUPT_CACHE.get(String.valueOf(taskDB.getTaskId()));
                if (!org.springframework.util.StringUtils.isEmpty(value)) {
                    Plc.PLC_INTERRUPT_CACHE.remove(String.valueOf(taskDB.getTaskId()));
                }
                // 当前任务有后置任务，把当前任务的后置任务设置为可预分车
                TaskDB sufTask = taskdbServiceImpl.findTaskByPreTaskId(taskDB.getTaskId());
                if (sufTask != null) {
                    taskdbServiceImpl.updateIsAllowAssignmentRgv(sufTask, true);
                }
                if (StringUtils.isNotBlank(taskDB.getWmsTaskId())) {
                    NodeDB nodeDBEnd = nodeService.getNodeDB(Node.parseGrid(taskDB.getEndNodeStr()));
                    toWMS.executionPhase(TaskStatusRtn.buildTaskStatusRtn(taskDB.getWmsTaskId(), nodeDBEnd.getGridId(), WmsTaskStatus.FINISH));
                }
            }
        } catch (Exception e) {
            log.error("任务" + taskDB + "执行Plc过程中出现异常" + e.getMessage(), e);
            // 前端小铃铛提示等待修改或者恢复
            NotificationBO notificationBO = new NotificationBO();
            notificationBO.setContent("任务" + taskDB.getTaskId() + "执行Plc过程中出现异常" + e.getMessage());
            notificationBO.setNotificationType(NotificationTypeEnum.ERROR.getType());
            notificationBO.setIsRead(false);
            notificationBO.setSeverityLevel(1);
            notificationService.insertNotification(notificationBO);
        }

    }

    /**
     * 把数据库中读出的字符串整合成可执行对象
     *
     * @param proGrammeArray
     * @return
     */
    public List<PathObject> zhengHe(String[] proGrammeArray, Integer curPlcProgress) {
        //判断任务到第几步标识位，用于plc阶段任务恢复
        int k = 0;
        if (curPlcProgress != TaskPlcProgress.ALL_DEVICE_COMPLETE.getProgressNum()
                && curPlcProgress != TaskPlcProgress.WAIT.getProgressNum()) {
            PlcDeviceDB plcdevicedb = plcDeviceDBService.queryPlcdevByDeviceId(curPlcProgress);
            for (int i = 0; i < proGrammeArray.length; i++) {
                if (plcdevicedb != null && proGrammeArray[i].equals(plcdevicedb.getDeviceId().toString())) {
                    k = i;
                }
            }
        }

        PathObject preObj = null;
        List<PathObject> pathObjects = new ArrayList<>();
        for (int i = 0; i < proGrammeArray.length; i++) {
            PathObject pathObject = analyzePathObj(proGrammeArray, preObj, i);

            if (pathObject != null) {
                if (i >= k) {
                    pathObjects.add(pathObject);
                }
                preObj = pathObject;
            }
        }
        return pathObjects;
    }

    private PathObject analyzePathObj(String[] proGrammeArray, PathObject preObj, int index) {
        PlcDeviceProcess plcDeviceProcess = PlcDeviceProcess.parseDeviceProcess(Integer.parseInt(proGrammeArray[index]));

        switch (plcDeviceProcess) {
            case INVALID:
                break;
            case CALL_RGV:
                return new PathObject(plcDeviceProcess.getId(), plcDeviceProcess.getId(), null, preObj);
            case FORWARD_TRANS_CARGO:
            case BACK_TRANS_CARGO:
            case TRANS_PLANTER_UP:
            case TRANS_PLANTER_DOWN:
            case LOCK_HAVE_CARGO:
            case LOCK_NO_CARGO:
            case UNLOCK:
                int start = Integer.parseInt(proGrammeArray[index]);
                if (index < proGrammeArray.length - 1) {
                    PlcDeviceDB deviceDB = plcdevicedbMapper.selectById(Integer.parseInt(proGrammeArray[index + 1]));
                    return new PathObject(start, start, deviceDB.getDeviceId(), preObj);
                } else {
                    throw new RuntimeException("加锁或解锁后面需给上设备");
                }
            default:
                throw new RuntimeException("错误的plc进度:" + plcDeviceProcess);
        }

        PlcDeviceDB p = plcdevicedbMapper.selectById(Integer.parseInt(proGrammeArray[index]));
        if (p.getDeviceType() == PlcDeviceType.CHAIN_STATION.getTypeNum()
                || p.getDeviceType() == PlcDeviceType.AUTO_TRAVERSING.getTypeNum()
                || p.getDeviceType() == PlcDeviceType.MONOMER_CONVEYOR.getTypeNum()
                || p.getDeviceType() == PlcDeviceType.TRANS_PLANTER.getTypeNum()
                || p.getDeviceType() == PlcDeviceType.FOLDING_MACHINE.getTypeNum()
        ) {
            if (index != proGrammeArray.length - 1) {
                PlcDeviceProcess deviceProcess = PlcDeviceProcess.parseDeviceProcess(
                        Integer.parseInt(proGrammeArray[index + 1]));
                if (deviceProcess != PlcDeviceProcess.INVALID) {
                    return null;
                }

                if (index > 0) {
                    //上个进程为下面的进度，当前进度为动作对应的设备，忽略
                    PlcDeviceProcess preDeviceProcess = PlcDeviceProcess.parseDeviceProcess(
                            Integer.parseInt(proGrammeArray[index - 1]));
                    switch (preDeviceProcess) {
                        case FORWARD_TRANS_CARGO:
                        case BACK_TRANS_CARGO:
                        case TRANS_PLANTER_UP:
                        case TRANS_PLANTER_DOWN:
                        case LOCK_HAVE_CARGO:
                        case LOCK_NO_CARGO:
                        case UNLOCK:
                            return null;
                        default:
                            break;
                    }
                }

                PlcDeviceDB p2 = plcdevicedbMapper.selectById(Integer.parseInt(proGrammeArray[index + 1]));
                if (p2.getDeviceType() == PlcDeviceType.CHAIN_STATION.getTypeNum()
                        || p2.getDeviceType() == PlcDeviceType.AUTO_TRAVERSING.getTypeNum()
                        || p2.getDeviceType() == PlcDeviceType.MONOMER_CONVEYOR.getTypeNum()
                        || p2.getDeviceType() == PlcDeviceType.TRANS_PLANTER.getTypeNum()
                        || p2.getDeviceType() == PlcDeviceType.FOLDING_MACHINE.getTypeNum()
                        || p2.getDeviceType() == PlcDeviceType.TOOTH_HOIST.getTypeNum()
                ) {
                    return new PathObject(p.getDeviceId(), p2.getDeviceId(), null, preObj);
                }
            }
        } else if (p.getDeviceType() == PlcDeviceType.PANASONIC_LIFT.getTypeNum() || p.getDeviceType() == PlcDeviceType.SIEMENS_HOIST.getTypeNum()
                || p.getDeviceType() == PlcDeviceType.TAMAGAWA_LIFTER.getTypeNum() || p.getDeviceType() == PlcDeviceType.AUTOMATIC_LIFTER.getTypeNum()
                || p.getDeviceType() == PlcDeviceType.FORK_LIFTER.getTypeNum()) {
            if (index != proGrammeArray.length -1){
                PlcDeviceProcess deviceProcess = PlcDeviceProcess.parseDeviceProcess(
                        Integer.parseInt(proGrammeArray[index + 1]));
                if (deviceProcess != PlcDeviceProcess.INVALID) {
                    preObj = findTruePreObj(preObj);
                    return new PathObject(p.getDeviceId(), p.getDeviceId(), null, preObj);
                } else {
                    PlcDeviceDB p2 = plcdevicedbMapper.selectById(Integer.parseInt(proGrammeArray[index + 1]));
                    return new PathObject(p.getDeviceId(), p2.getDeviceId(), null, preObj);
                }
            } else {
                return new PathObject(p.getDeviceId(), p.getDeviceId(), null, preObj);
            }
        }

        return null;
    }

    /**
     * 查找正确的前置obj
     * @param preObj
     * @return
     */
    private PathObject findTruePreObj(PathObject preObj) {
        if (preObj == null) {
            return null;
        }

        while (true) {
            PlcDeviceProcess startDeviceProcess = PlcDeviceProcess.parseDeviceProcess(preObj.start);
            PlcDeviceProcess endDeviceProcess = PlcDeviceProcess.parseDeviceProcess(preObj.end);

            if (startDeviceProcess == PlcDeviceProcess.INVALID && endDeviceProcess == PlcDeviceProcess.INVALID) {
                break;
            }

            if (preObj.preObj == null) {
                break;
            }

            preObj = preObj.preObj;
        }

        return preObj;
    }

    /**
     * 根据方案执行Plc流程
     *
     * @param programDB
     * @throws IOException
     */
    public void findProgramme(ProgrammeDB programDB) throws IOException, InterruptedException {
        if (StringUtils.isNotEmpty(programDB.getProgramme())) {
            String[] proGrammeArray = programDB.getProgramme().split("-");
            List<PathObject> pathObjects = zhengHe(proGrammeArray, taskDB.getPlcProgress());
            if (pathObjects == null) {
                throw new RuntimeException(programDB.getProgrammeName() + "口路径拆分对象为空");
            }
            for (PathObject pathObject : pathObjects) {
                log.info(pathObjects.toString());
                if (pathObject.getStart().equals(PlcDeviceProcess.CALL_RGV.getId())) {
                    taskdbServiceImpl.updateIsAllowAssignmentRgv(taskDB, true);
                } else if (pathObject.getStart().equals(PlcDeviceProcess.LOCK_HAVE_CARGO.getId())) {
                    lockDevice(pathObject.end, taskDB, true, pathObject.nextObj);
                } else if (pathObject.getStart().equals(PlcDeviceProcess.LOCK_NO_CARGO.getId())) {
                    lockDevice(pathObject.end, taskDB, false, pathObject.nextObj);
                } else if (pathObject.getStart().equals(PlcDeviceProcess.UNLOCK.getId())) {
                    unLockDevice(pathObject.end, taskDB, pathObject.nextObj);
                } else if (pathObject.getStart().equals(PlcDeviceProcess.FORWARD_TRANS_CARGO.getId())) {
                    deviceTransCargo(pathObject.end, taskDB, pathObject.nextObj, true);
                } else if (pathObject.getStart().equals(PlcDeviceProcess.BACK_TRANS_CARGO.getId())) {
                    deviceTransCargo(pathObject.end, taskDB, pathObject.nextObj, false);
                } else if (pathObject.getStart().equals(PlcDeviceProcess.TRANS_PLANTER_UP.getId())) {
                    transPlanterLift(pathObject.end, taskDB, pathObject.nextObj, true);
                } else if (pathObject.getStart().equals(PlcDeviceProcess.TRANS_PLANTER_DOWN.getId())) {
                    transPlanterLift(pathObject.end, taskDB, pathObject.nextObj, false);
                } else {
                    PlcDeviceDB startPlcDb = plcdevicedbMapper.selectById(pathObject.start);
                    PlcDeviceDB endPlcDb = plcdevicedbMapper.selectById(pathObject.end);
                    if (startPlcDb.getDeviceType() == PlcDeviceType.CHAIN_STATION.getTypeNum()) {
                        TaskDetailDB taskDetailDB = taskDetailService.getTaskDetail(taskDB.getTaskId(), DetailProcess.STATION_TASK, null, startPlcDb.getDeviceName(), endPlcDb.getDeviceName(), PrecessStatus.CONDUCT,
                                null, Joiner.on("-").join(Arrays.asList(PrecessOperation.INTERRUPT.getValue(), PrecessOperation.RECOVER.getValue())));
                        StationOper stationOper = (StationOper) Plc.deviceBaseNameMap.get(startPlcDb.getDeviceName());
                        StationOper stationOper1 = (StationOper) Plc.deviceBaseNameMap.get(endPlcDb.getDeviceName());
                        taskdbServiceImpl.updateTaskPlcProgress(taskDB, startPlcDb.getDeviceId());
                        ConveyEvent.conveyRun(taskDB, taskDB.getPlcId(), stationOper, stationOper1, taskDetailDB);
                        taskdbServiceImpl.updateTaskPlcProgress(taskDB, endPlcDb.getDeviceId());
                        taskDetailService.updateTaskDetailCompleteTime(taskDetailDB);
                        log.info("调用" + startPlcDb.getDeviceName() + "成功,当前任务为" + taskDB);
                    } else if (startPlcDb.getDeviceType() == PlcDeviceType.PANASONIC_LIFT.getTypeNum()
                            || startPlcDb.getDeviceType() == PlcDeviceType.SIEMENS_HOIST.getTypeNum()
                            || startPlcDb.getDeviceType() == PlcDeviceType.TAMAGAWA_LIFTER.getTypeNum()
                            || startPlcDb.getDeviceType() == PlcDeviceType.AUTOMATIC_LIFTER.getTypeNum()
                            || startPlcDb.getDeviceType() == PlcDeviceType.FORK_LIFTER.getTypeNum()) {
                        Lifter lifter = nodeService.getLifter(Node.parseGrid(startPlcDb.getPlcLockLocation()));
                        LifterOper lifterOper = chooseLifter(startPlcDb.getDeviceName());
                        LifterEventInterface lifterEvent = SystemInit.getLifterEvent(lifterOper);
                        PlcDeviceDB lifterDeviceDB = plcDeviceDBService.getLifterDeviceDB(lifterOper.getPlcDeviceType().getTypeNum(), lifterOper.getLockLocation());
                        Integer startLayer = taskDB.getTaskType().equals(TaskType.IN) ? programDB.getProgrammeFloor() : taskDB.selectEndNode().getZ();
                        //目的位置如果是提升机内部则不去目标层
                        Integer endLayer = taskDB.getTaskType().equals(TaskType.IN) ? getEndLayer(programDB) : programDB.getProgrammeFloor();
                        Location startLocation = taskDB.getTaskType().equals(TaskType.IN) ? Location.parseLocationCode(startPlcDb.getLocation())
                                : Location.parseLocationCode(chooseLocationByCoordEnd(taskDB.getEndNodeStr()));
                        Location endLocation = taskDB.getTaskType().equals(TaskType.IN) ? Location.parseLocationCode(chooseLocationByCoordEnd(taskDB.getStartNodeStr()))
                                : Location.parseLocationCode(startPlcDb.getLocation());
                        Node node = Node.parseGrid(startPlcDb.getDeviceLocation());
                        NodeDB endNodeDB = nodeService.getNodeDB(taskDB.getTaskType().equals(TaskType.IN) ? Node.parseGrid(taskDB.getStartNodeStr()) : new Node(node.getX(), node.getY(), endLayer));
                        TaskDetailDB taskDetailDB = taskDetailService.getTaskDetail(taskDB.getTaskId(), DetailProcess.HOIST_TASK, null,
                                startLayer + startLocation.getDescription(), endLayer + endLocation.getDescription(), PrecessStatus.CONDUCT, lifterDeviceDB.getDeviceId().toString(),
                                Joiner.on("-").join(Arrays.asList(PrecessOperation.INTERRUPT.getValue(), PrecessOperation.INIT.getValue(), PrecessOperation.RECOVER.getValue())));
                        boolean needLock = true;
                        while (needLock) {
                            // 先检测条件后加锁，然后再检测条件
                            log.info(lifterOper.getDeviceName() + "提升机目的位置光电有货?: " + lifterOper.isPalletExisted(endLayer, endLocation)
                                    + "提升机就绪?: " + lifterOper.isReady() + "提升机有任务?: " + (lifterOper.getHoistCurrentTaskNo() != 0)
                                    + "提升机内有车?: " + lifterOper.isHaveShuttle() + "提升机目的位置有货?: " + checkNodeStatus(endNodeDB));
                            checkStatus(lifterOper, endLayer, endLocation, endNodeDB, lifterEvent);
                            checkLifterLock(lifter, lifterOper);
                            LockNodeManager.lockLifter(lifter, taskDB);

                            // 不可用则解除锁定后重新去抢锁
                            if (!afterLockCheckStatus(lifterOper, endNodeDB)) {
                                log.info("锁定后提升机不可用：" + lifterOper.getDeviceName() + "提升机就绪?: " + lifterOper.isReady() + "提升机有任务?: " + (lifterOper.getHoistCurrentTaskNo() != 0)
                                        + "提升机内有车?: " + lifterOper.isHaveShuttle() + "提升机目的位置有货?: " + checkNodeStatus(endNodeDB));
                                LockNodeManager.unlockLifter(lifter, taskDB);
                            } else {
                                needLock = false;
                            }
                            Thread.sleep(1000);
                        }
                        TaskDetailDB lockDetail = taskDetailService.getTaskDetail(taskDB.getTaskId(), DetailProcess.LOCK_HOIST, taskDetailDB.getUuid(), taskDetailDB.getStart(), taskDetailDB.getEnding(),
                                PrecessStatus.CONDUCT, null, null);
                        taskDetailService.updateTaskDetailCompleteTime(lockDetail);
                        checkStatus(lifterOper, endLayer, endLocation, endNodeDB, lifterEvent);
                        // 入库锁定提升机之后再允许分车
                        if (taskDB.getTaskType().equals(TaskType.IN)) {
                            taskdbServiceImpl.updateIsAllowAssignmentRgv(taskDB, true);
                        }
                        Node startNode = Node.parseGrid(startPlcDb.getDeviceLocation());
                        lifterEvent.palletTask(lifterOper, lifter, taskDB, startNode, startLayer, endLayer, startLocation, endLocation, taskDetailDB);
                        TaskDetailDB unlockDetail = taskDetailService.buildTask(taskDB.getTaskId(), DetailProcess.UNLOCK_HOIST, taskDetailDB.getUuid(), taskDetailDB.getStart(), taskDetailDB.getEnding(),
                                PrecessStatus.CONDUCT, lifterDeviceDB.getDeviceId().toString(), PrecessOperation.UNLOCK.getValue().toString());
                        unlockDetail.setRemark(taskDB.getPlcId().toString());
                        taskDetailService.updateById(unlockDetail);
                        LockNodeManager.unlockLifter(lifter, taskDB);
                        taskDetailService.updateTaskDetailCompleteTime(unlockDetail);
                        log.info("调用" + startPlcDb.getDeviceName() + "成功,当前任务为" + taskDB);
                        taskDetailService.updateTaskDetailCompleteTime(taskDetailDB);
                        TaskDetailDB endDet = taskDetailService.buildTask(taskDB.getTaskId(), DetailProcess.HOIST_UPDATE_Y, taskDetailDB.getUuid(), taskDetailDB.getStart(), taskDetailDB.getEnding(),
                                PrecessStatus.CONDUCT, null, PrecessOperation.UPDATE_Y.getValue().toString());
                        // 接驳点是提升机左右，则需要更新接驳点是否有货
                        if (TaskType.IN.equals(taskDB.getTaskType())) {
                            nodeService.updateLifterLeftOrRightNodeDB(taskDB.selectStartNode(), NodeStatus.Y.toString());
                            endDet.setDeviceInfo(taskDB.getStartNodeStr());
                        } else if (TaskType.OUT.equals(taskDB.getTaskType())) {
                            nodeService.updateLifterLeftOrRightNodeDB(new Node(startNode.getX(), startNode.getY(), endLayer), NodeStatus.Y.toString());
                            endDet.setDeviceInfo(new Node(startNode.getX(), startNode.getY(), endLayer).toString());
                        }
                        taskDetailService.updateById(endDet);
                        taskDetailService.updateTaskDetailCompleteTime(endDet);
                    } else if (startPlcDb.getDeviceType().equals(PlcDeviceType.AUTO_HOIST.getTypeNum())
                            || startPlcDb.getDeviceType().equals(PlcDeviceType.AUTO_TRAVERSING.getTypeNum())
                            || startPlcDb.getDeviceType().equals(PlcDeviceType.MONOMER_CONVEYOR.getTypeNum())
                            || startPlcDb.getDeviceType().equals(PlcDeviceType.TRANS_PLANTER.getTypeNum())
                            || startPlcDb.getDeviceType().equals(PlcDeviceType.FOLDING_MACHINE.getTypeNum())
                    ) {
                        TaskDetailDB taskDetailDB = taskDetailService.getTaskDetail(taskDB.getTaskId(), DetailProcess.STATION_TASK, null, startPlcDb.getDeviceName(), endPlcDb.getDeviceName(), PrecessStatus.CONDUCT,
                                null, Joiner.on("-").join(Arrays.asList(PrecessOperation.INTERRUPT.getValue(), PrecessOperation.RECOVER.getValue())));
                        DeviceBase stationOper = Plc.deviceBaseNameMap.get(startPlcDb.getDeviceName());
                        taskdbServiceImpl.updateTaskPlcProgress(taskDB, startPlcDb.getDeviceId());
                        //直接更新点位为无货
                        nodeService.updateNodeDBStatus(stationOper.getCoord(), NodeStatus.N.toString());
                        DeviceInterface startDevice = (DeviceInterface) stationOper;
                        DeviceInterface endDevice = (DeviceInterface) Plc.deviceBaseNameMap.get(endPlcDb.getDeviceName());
                        PlcConveryTask.plcTransCargo(startDevice, endDevice, taskDB,
                                taskDetailDB);
                        taskdbServiceImpl.updateTaskPlcProgress(taskDB, endPlcDb.getDeviceId());
                        taskDetailService.updateTaskDetailCompleteTime(taskDetailDB);
                        log.info("调用" + startPlcDb.getDeviceName() + "成功,当前任务为" + taskDB);
                    }
                }
            }
        }
        taskdbServiceImpl.updateTaskPlcProgress(taskDB, TaskPlcProgress.ALL_DEVICE_COMPLETE.getProgressNum());
        Plc.PLC_INTERRUPT_CACHE.remove(String.valueOf(taskDB.getTaskId()));
    }

    /**
     * 锁定设备位置
     * @param deviceId
     * @param taskDB
     * @param haveCargo
     * @param nextObj
     */
    private void lockDevice(Integer deviceId, TaskDB taskDB, boolean haveCargo, Integer nextObj) {
        PlcDeviceDB nextDevice = plcdevicedbMapper.selectById(nextObj);
        DeviceBase deviceBase = Plc.deviceBaseNameMap.get(nextDevice.getDeviceName());
        Node node = new Node(deviceBase.getCoord());
        LoggerUtil.info("等待锁定位置:" + node, this.getClass());
        if (haveCargo) {
            while (true) {
                Plc.checkPLCInterrupt(taskDB.getTaskId().toString(), "锁定位置");
                if (!WcsUtil.isNodeHasCargo(node, nodeService) || !nodeService.isCanGet(node)) {
                    deviceBase.setDeviceStatus("无货?不可取?，无法锁定位置");
                    WcsFuncUtil.delay(1000);
                    continue;
                }

                if (LockNodeManager.lockedNodesSafe(Collections.singleton(node), taskDB)) {
                    break;
                }
                deviceBase.setDeviceStatus("被锁定，等待位置被释放");

                WcsFuncUtil.delay(1000);
            }
        } else {
            while (true) {
                Plc.checkPLCInterrupt(taskDB.getTaskId().toString(), "锁定位置");
                if (WcsUtil.isNodeHasCargo(node, nodeService) || !nodeService.isCanPut(node)) {
                    deviceBase.setDeviceStatus("有货?不可放?，无法锁定位置");
                    WcsFuncUtil.delay(1000);
                    continue;
                }

                if (LockNodeManager.lockedNodesSafe(Collections.singleton(node), taskDB)) {
                    break;
                }
                deviceBase.setDeviceStatus("被锁定，等待位置被释放");

                WcsFuncUtil.delay(1000);
            }
        }

        deviceBase.setDeviceStatus("");
        LoggerUtil.info("锁定位置成功:" + node, this.getClass());
    }

    /**
     * 解锁设备位置
     * @param deviceId
     * @param taskDB
     * @param nextObj
     */
    private void unLockDevice(Integer deviceId, TaskDB taskDB, Integer nextObj) {
        if (deviceId == null) {
            throw new RuntimeException("未配置需要解锁的设备, 任务id:" + taskDB.getTaskId());
        }
        PlcDeviceDB nextDevice = plcdevicedbMapper.selectById(nextObj);
        Node node = new Node(Plc.deviceBaseNameMap.get(nextDevice.getDeviceName()).getCoord());
        LockNodeManager.unlockedNodes(Collections.singleton(node), taskDB);
        LoggerUtil.info("解锁位置成功:" + node, this.getClass());
        if (nextObj != null) {
            taskdbServiceImpl.updateTaskPlcProgress(taskDB, nextObj);
        }
    }

    /**
     * 设备运输货物
     * @param deviceId
     * @param taskDB
     * @param nextObj
     * @param forwardRun
     */
    private void deviceTransCargo(Integer deviceId, TaskDB taskDB, Integer nextObj, boolean forwardRun) throws IOException {
        PlcDeviceDB deviceDB = plcdevicedbMapper.selectById(nextObj);
        if (deviceDB.getDeviceType() != PlcDeviceType.MONOMER_CONVEYOR.getTypeNum()) {
            throw new RuntimeException("方案配置错误, 输送线正传反转后面需要配置对应的输送线设备");
        }

        MonomerConveyorOper conveyorOper = (MonomerConveyorOper) Plc.deviceMonomerConveyorCoordMap
                .get(Node.parseGrid(deviceDB.getDeviceLocation()));

        while (!conveyorOper.canControl()) {
            Plc.checkPLCInterrupt(taskDB.getTaskId().toString(), "设备当前不可控制");
            conveyorOper.setDeviceStatus("设备当前不可控制，请查看设备是否就绪，运行，故障等状态");
            WcsFuncUtil.delay(1000);
        }
        conveyorOper.setDeviceStatus("");

        conveyorOper.transCargo(forwardRun);

        int tick = 10;
        int count = 2;
        while (!conveyorOper.isRunning()) {
            conveyorOper.setDeviceStatus("已发指令，等待运行");
            if (tick == 0) {
                LoggerUtil.info(conveyorOper.getDeviceName() + " 未检测到输送线运行，重发转动任务:" + count,
                        this.getClass());
                conveyorOper.transCargo(forwardRun);
                count --;
                if (count == 0) {
                    break;
                } else {
                    count --;
                }
            } else {
                tick --;
            }

            WcsFuncUtil.delay(1000);
        }

        tick = 60;
        while (!conveyorOper.isTransCargoEnd(forwardRun)) {
            Plc.checkPLCInterrupt(taskDB.getTaskId().toString(), "设备运输货物, 等待运输完成");
            if (tick == 0) {
                throw new RuntimeException("输送线转动未完成，plc任务失败");
            } else {
                tick--;
            }

            WcsFuncUtil.delay(1000);
        }
        conveyorOper.setDeviceStatus("");

        if (nextObj != null) {
            taskdbServiceImpl.updateTaskPlcProgress(taskDB, nextObj);
        }

        conveyorOper.clearTransEndSig();
    }

    /**
     * 移栽机升降
     * @param deviceId
     * @param taskDB
     * @param nextObj
     * @param up
     */
    private void transPlanterLift(Integer deviceId, TaskDB taskDB, Integer nextObj, boolean up) throws IOException {
        PlcDeviceDB deviceDB = plcdevicedbMapper.selectById(deviceId);
        if (deviceDB.getDeviceType() != PlcDeviceType.TRANS_PLANTER.getTypeNum()) {
            throw new RuntimeException("方案配置错误, 移栽机升降后面需要配置对应的移栽机设备");
        }

        TransPlanter transPlanter = (TransPlanter) Plc.deviceTransPlanterCoordMap
                .get(Node.parseGrid(deviceDB.getDeviceLocation()));

        while (!transPlanter.canControl()) {
            transPlanter.setDeviceStatus("设备当前不可控制，请查看设备是否就绪，运行，故障等状态");
            WcsFuncUtil.delay(1000);
        }
        transPlanter.setDeviceStatus("");

        if (up && transPlanter.isRiseInPlace()) {
            LoggerUtil.info(transPlanter.getDeviceName() + "上升到位，无需上升", this.getClass());
            return;
        } else if (!up && transPlanter.isDropInPlace()) {
            LoggerUtil.info(transPlanter.getDeviceName() + "下降到位，无需下降", this.getClass());
            return;
        }

        if (up) {
            transPlanter.platformRise();
        } else {
            transPlanter.platformDrop();
        }

        int tick = 60;
        while (true) {
            Plc.checkPLCInterrupt(taskDB.getTaskId().toString(), "设备运输货物, 等待运输完成");
            if (tick == 0) {
                transPlanter.setDeviceStatus("");
                throw new RuntimeException("输送线转动未完成，plc任务失败");
            } else {
                tick--;
            }

            if (up && transPlanter.isRiseInPlace()) {
                LoggerUtil.info(transPlanter.getDeviceName() + "上升到位", this.getClass());
                break;
            } else if (!up && transPlanter.isRiseInPlace()) {
                LoggerUtil.info(transPlanter.getDeviceName() + "下降到位", this.getClass());
                break;
            }

            transPlanter.setDeviceStatus("等待" + (up ? "上升" : "下降") + "到位");
            WcsFuncUtil.delay(1000);
        }
        transPlanter.setDeviceStatus("");

        if (nextObj != null) {
            taskdbServiceImpl.updateTaskPlcProgress(taskDB, nextObj);
        }
    }

    /**
     * 检测提升机被锁定
     *
     * @param lifter
     * @param lifterOper
     */
    private void checkLifterLock(Lifter lifter, LifterOper lifterOper) {
        while (LockNodeManager.checkLifterLocked(lifter, taskDB)) {
            Plc.checkPLCInterrupt(taskDB.getTaskId().toString(), "PlcTaskThread2");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //任务之前锁定提升机起点与终点
        if (lifterOper instanceof TwoDirectionRgvLifterOper) {
            LifterPathNode.taskBefore(taskDB);
        }
    }

    private boolean checkNodeStatus(NodeDB endNodeDB) {
        if (endNodeDB == null) {
            return false;
        } else {
            return NodeStatus.Y.toString().equals(endNodeDB.getGridStatus());
        }
    }

    /**
     * 检测是否满足调用提升机的条件
     *  @param lifterOper
     * @param endLayer
     * @param endLocation
     * @param endNodeDB
     * @param lifterEvent
     */
    private void checkStatus(LifterOper lifterOper, Integer endLayer, Location endLocation, NodeDB endNodeDB,
                             LifterEventInterface lifterEvent) {
        boolean endHasCargo = lifterEvent.isPalletExisted(lifterOper, endLayer, endLocation);
        while (endHasCargo || !lifterOper.isReady()
                || (lifterOper.getHoistCurrentTaskNo() != 0) || lifterOper.isHaveShuttle() || checkNodeStatus(endNodeDB)) {
            log.info(lifterOper.getDeviceName() + "提升机目的位置光电有货?: " + endHasCargo
                    + "提升机就绪?: " + lifterOper.isReady() + "提升机有任务?: " + (lifterOper.getHoistCurrentTaskNo() != 0)
                    + "提升机内有车?: " + lifterOper.isHaveShuttle() + "提升机目的位置有货?: " + checkNodeStatus(endNodeDB));
            if (endNodeDB != null) {
                endNodeDB = nodeService.getNodeDB(new Coord(endNodeDB.getGridx(), endNodeDB.getGridy(), endNodeDB.getGridz()));
            }
            Plc.checkPLCInterrupt(taskDB.getTaskId().toString(), "PlcTaskThread1");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            endHasCargo = lifterEvent.isPalletExisted(lifterOper, endLayer, endLocation);
        }
    }

    /**
     * 工位行驶任务Plc流程
     *
     * @param
     * @throws IOException
     */
    public void moveStationPlc() throws IOException {
        Node start = Node.parseGrid(taskDB.getStartNodeStr());
        Node end = Node.parseGrid(taskDB.getEndNodeStr());
        LifterOper lifterOper = (LifterOper) Plc.deviceLifterCoordMap.get(new Coord(start.getX(), start.getY(), 1));
        LifterOper lifterOper1 = (LifterOper) Plc.deviceLifterCoordMap.get(new Coord(end.getX(), end.getY(), 1));
        if (lifterOper == null || lifterOper1 == null) {
            StationOper stationOper = (StationOper) Plc.deviceStationCoordMap.get(start);
            StationOper stationOper1 = (StationOper) Plc.deviceStationCoordMap.get(end);
            TaskDetailDB taskDetailDB = taskDetailService.getTaskDetail(taskDB.getTaskId(), DetailProcess.STATION_TASK, null, stationOper.getDeviceName(), stationOper1.getDeviceName(), PrecessStatus.CONDUCT,
                    null, Joiner.on("-").join(Arrays.asList(PrecessOperation.INTERRUPT.getValue(), PrecessOperation.RECOVER.getValue())));
            ConveyEvent.conveyRun(taskDB, taskDB.getPlcId(), stationOper, stationOper1, taskDetailDB);
            taskDetailService.updateTaskDetailCompleteTime(taskDetailDB);
            log.info("调用" + stationOper.getDeviceName() + "成功,当前任务为" + taskDB);
        } else {
            Lifter lifter = nodeService.getLifter(Node.parseGrid(lifterOper.getLockLocation().toString()));
            LifterEventInterface lifterEvent = SystemInit.getLifterEvent(lifterOper);
            Integer startLayer = taskDB.selectStartNode().getZ();
            //目的位置如果是提升机内部则不去目标层
            Integer endLayer = taskDB.selectEndNode().getZ();
            Location startLocation = Location.parseLocationCode(chooseLocationByCoordEnd(taskDB.getStartNodeStr()));
            Location endLocation = Location.parseLocationCode(chooseLocationByCoordEnd(taskDB.getEndNodeStr()));
            TaskDetailDB taskDetailDB = taskDetailService.getTaskDetail(taskDB.getTaskId(), DetailProcess.HOIST_TASK, null,
                    startLayer + startLocation.getDescription(), endLayer + endLocation.getDescription(), PrecessStatus.CONDUCT, null,
                    Joiner.on("-").join(Arrays.asList(PrecessOperation.INTERRUPT.getValue(), PrecessOperation.INIT.getValue(), PrecessOperation.RECOVER.getValue())));
            // 先检测条件后加锁，然后再检测条件
            log.info(lifterOper.getDeviceName() + "提升机目的位置有货?: " + lifterOper.isPalletExisted(endLayer, endLocation)
                    + "提升机就绪?: " + lifterOper.isReady() + "提升机有任务?: " + (lifterOper.getHoistCurrentTaskNo() != 0)
                    + "提升机内有车?: " + lifterOper.isHaveShuttle());
            while (lifterOper.isPalletExisted(endLayer, endLocation) || !lifterOper.isReady()
                    || (lifterOper.getHoistCurrentTaskNo() != 0) || lifterOper.isHaveShuttle()) {
                log.info(lifterOper.getDeviceName() + "提升机目的位置有货?: " + lifterOper.isPalletExisted(endLayer, endLocation)
                        + "提升机就绪?: " + lifterOper.isReady() + "提升机有任务?: " + (lifterOper.getHoistCurrentTaskNo() != 0)
                        + "提升机内有车?: " + lifterOper.isHaveShuttle());
                Plc.checkPLCInterrupt(taskDB.getTaskId().toString(), "PlcTaskThread1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            checkLifterLock(lifter, lifterOper);
            TaskDetailDB lockDetail = taskDetailService.getTaskDetail(taskDB.getTaskId(), DetailProcess.LOCK_HOIST, taskDetailDB.getUuid(), taskDetailDB.getStart(), taskDetailDB.getEnding(),
                    PrecessStatus.CONDUCT, null, null);
            LockNodeManager.lockLifter(lifter, taskDB);
            taskDetailService.updateTaskDetailCompleteTime(lockDetail);
            Node startNode = Node.parseGrid(taskDB.getEndNodeStr());
            lifterEvent.palletTask(lifterOper, lifter, taskDB, startNode, startLayer, endLayer, startLocation, endLocation, taskDetailDB);
            PlcDeviceDB lifterDeviceDB = plcDeviceDBService.getLifterDeviceDB(lifterOper.getPlcDeviceType().getTypeNum(), lifterOper.getLockLocation());
            TaskDetailDB unlockDetail = taskDetailService.buildTask(taskDB.getTaskId(), DetailProcess.UNLOCK_HOIST, taskDetailDB.getUuid(), taskDetailDB.getStart(), taskDetailDB.getEnding(),
                    PrecessStatus.CONDUCT, lifterDeviceDB.getDeviceId().toString(), PrecessOperation.UNLOCK.getValue().toString());
            LockNodeManager.unlockLifter(lifter, taskDB);
            taskDetailService.updateTaskDetailCompleteTime(unlockDetail);
            log.info("调用" + lifterOper.getDeviceName() + "成功,当前任务为" + taskDB);
            taskDetailService.updateTaskDetailCompleteTime(taskDetailDB);
        }
        taskdbServiceImpl.updateTaskPlcProgress(taskDB, TaskPlcProgress.ALL_DEVICE_COMPLETE.getProgressNum());
        Plc.PLC_INTERRUPT_CACHE.remove(String.valueOf(taskDB.getTaskId()));
        if (taskDB.getTaskType().equals(TaskType.OUT)) {
            if (StringUtils.isNotBlank(taskDB.getWmsTaskId())) {
                toWMS.executionPhase(TaskStatusRtn.buildTaskStatusRtn(taskDB.getWmsTaskId(), WmsTaskStatus.FINISH));
            }
        }
    }

    /**
     * 入库任务获取进提升机之后去的层
     * 如果是提升机内部在当前层等待，否则直接去目的层
     *
     * @param programmeDB
     * @return
     */
    private Integer getEndLayer(ProgrammeDB programmeDB) {
        return Location.parseLocationCode(chooseLocationByCoordEnd(taskDB.getStartNodeStr()))
                == Location.INSIDE ? programmeDB.getProgrammeFloor() : taskDB.selectEndNode().getZ();
    }

    //通过名字查找提升机Oper
    public LifterOper chooseLifter(String lifterName) {
        LifterOper lifterOper = (LifterOper) Plc.deviceBaseNameMap.get(lifterName);
        return lifterOper;
    }

    //查找提升机的终点
    public int chooseLocationByCoordEnd(String nodeStr) {
        DeviceBase pb = Plc.deviceLifterCoordMap.get(new Coord(Node.parseGrid(nodeStr).getX(), Node.parseGrid(nodeStr).getY(), 1));
        return pb.getLocation().getCode();
    }

    /**
     * 锁定后检测提升机是否可用
     *
     * @param lifterOper
     * @param endNodeDB
     * @return
     */
    private boolean afterLockCheckStatus(LifterOper lifterOper, NodeDB endNodeDB) {
        if (endNodeDB != null) {
            endNodeDB = nodeService.getNodeDB(new Coord(endNodeDB.getGridx(), endNodeDB.getGridy(), endNodeDB.getGridz()));
        }
        if (!lifterOper.isReady() || (lifterOper.getHoistCurrentTaskNo() != 0) || lifterOper.isHaveShuttle() || checkNodeStatus(endNodeDB)) {
            log.info(lifterOper.getDeviceName() + "提升机就绪?: " + lifterOper.isReady() + "提升机有任务?: " + (lifterOper.getHoistCurrentTaskNo() != 0)
                    + "提升机内有车?: " + lifterOper.isHaveShuttle() + "目的位置有货?：" + checkNodeStatus(endNodeDB));
            return false;
        }
        return true;
    }
}


